package cn.virens.common.boot.dubbo.network;

import cn.virens.common.util.exception.APIException;
import cn.virens.common.util.exception.ExceptionUtil;
import org.apache.dubbo.common.utils.NetUtils;
import org.dromara.hutool.core.net.NetUtil;
import org.dromara.hutool.core.reflect.method.MethodUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.text.split.SplitUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;
import java.util.List;
import java.util.regex.Pattern;

import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_IP_TO_BIND;

public class DubboNet {
    private static final Pattern IP_PATTERN = Pattern.compile("\\d{1,3}(\\.\\d{1,3}){3,5}$");

    private static Logger logger = LoggerFactory.getLogger(DubboNet.class);

    private static final String DUBBO_IP_IGNORE = "DUBBO_IP_IGNORE";

    private static final String DOCKER_GWBRIDGE = "172.18.";
    private static final String DOCKER_INGRESS = "10.0.0.";

    /**
     * 初始化dubbo默认绑定的IP地址(是否覆盖环境变量)
     */
    public static void initDubboIpToBind(boolean replace) throws APIException {
        String last = System.getProperty(DUBBO_IP_TO_BIND);
        if (last == null || last.isEmpty() || replace) {
            setDubboIpToBind(getIpByDocker());
        }
    }

    /**
     * 如果在docker环境就根据hostname获取对应ip，否则按正常方式获取
     */
    private static String getIpByDocker() throws APIException {
        String hostname = getHostNameByDocker();

        if (hostname != null && !hostname.isEmpty()) {
            return NetUtil.getIpByHost(hostname);//
        } else {
            return getLocalHost();
        }
    }

    /**
     * 获取在docker环境中的hostname
     */
    private static String getHostNameByDocker() throws APIException {
        String hostname = System.getProperty("HOSTNAME");
        if (hostname == null || hostname.isEmpty()) {
            return System.getenv("HOSTNAME");
        } else {
            return hostname;
        }
    }

    /**
     * 必须要在dubbo应用启动之前调用
     */
    private static void setDubboIpToBind(String host) throws APIException {
        System.setProperty(DUBBO_IP_TO_BIND, host);
    }

    /**
     * 获取dubbo默认绑定的IP地址
     */
    public static String getDubboIpToBind() throws APIException {
        return System.getProperty(DUBBO_IP_TO_BIND);
    }

    /**
     * 获取当前有效的IP，主要是屏蔽了docker_gwbridge的IP端
     */
    public static String getLocalHost() throws APIException {
        for (NetworkInterface network : getNetworkInterfaces()) {
            String host = getHostAddress(network);//

            if (host != null && !host.isEmpty()) {
                return host;//
            }
        }

        return NetUtil.getLocalhostStrV4();
    }

    private static String getHostAddress(NetworkInterface inter) throws APIException {
        Enumeration<InetAddress> enumer = inter.getInetAddresses();

        while (enumer != null && enumer.hasMoreElements()) {
            InetAddress addr = enumer.nextElement();

            if (Boolean.TRUE.equals(isValidHost(addr))) {
                return addr.getHostAddress();//
            }
        }

        return null;
    }

    /**
     * 获取有效的网卡列表
     */
    private static List<NetworkInterface> getNetworkInterfaces() throws APIException {
        return ExceptionUtil.safe1(logger, () -> invokeStatic("getValidNetworkInterfaces"));
    }

    /**
     * 验证地址是否有效（是否可到达）
     */
    private static Boolean isValidHost(InetAddress addr) throws APIException {
        if (addr != null && isValidHost(addr.getHostAddress())) {
            return ExceptionUtil.safe1(logger, () -> {
                return addr.isReachable(100);
            });
        } else {
            return false;
        }
    }

    /**
     * 验证地址是否有效（IP格式正确）
     */
    private static boolean isValidHost(String addr) throws APIException {
        if (addr != null && IP_PATTERN.matcher(addr).matches()) {
            for (String ipIgnore : loadIpIgnoreArray()) {
                if (StrUtil.startWith(addr, ipIgnore)) {
                    return false;//
                }
            }

            return NetUtils.isValidLocalHost(addr);
        } else {
            return false;
        }
    }

    /**
     * 获取动态配置的忽略IP地址范围
     */
    private static String[] loadIpIgnoreArray() throws APIException {
        String cnf_ip_ignore = System.getProperty(DUBBO_IP_IGNORE);
        if (cnf_ip_ignore != null && !cnf_ip_ignore.isEmpty()) {
            return SplitUtil.splitToArray(cnf_ip_ignore, ",");
        }

        return new String[]{DOCKER_GWBRIDGE, DOCKER_INGRESS};
    }

    /**
     * 调用Dubbo.NetUtils的私有静态方法
     */
    @SuppressWarnings("unchecked")
    private static <R> R invokeStatic(String methodName) throws APIException {
        Method method = MethodUtil.getMethodByName(NetUtils.class, methodName);

        if (method != null) {
            return (R) MethodUtil.invokeStatic(method);
        } else {
            return null;
        }
    }
}
